package hamaster.jpbc.gui;

import static hamaster.jpbc.gui.Messages.getString;
import static hamaster.jpbc.userconfig.ConfigKeys.DEFAULT_ID_FILE;
import static hamaster.jpbc.userconfig.ConfigKeys.LAST_CIPHER_SAVE_FOLDER;
import static hamaster.jpbc.userconfig.ConfigKeys.LAST_PLAIN_OPEN_FOLDER;
import static java.awt.FlowLayout.LEFT;
import static java.awt.FlowLayout.RIGHT;
import static javax.swing.BoxLayout.Y_AXIS;
import static javax.swing.JOptionPane.showConfirmDialog;
import static javax.swing.JOptionPane.showMessageDialog;
import hamaster.jpbc.Main;
import hamaster.jpbc.crypt.CryptMethod;
import hamaster.jpbc.gui.callback.AsynchronousCallback;
import hamaster.jpbc.mgr.AsymmetricEncryptManager;
import hamaster.jpbc.mgr.SymmetricEncryptManager;
import hamaster.jpbc.pbc.io.IBCIOException;
import hamaster.jpbc.pbc.io.IDIO;
import hamaster.jpbc.pbc.io.SimpleIDFileIOImpl;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 * 加密操作面板
 * @author <a href="mailto:wangyeee@gmail.com">Wang Ye</a>
 */
public class EncryptPanel extends JPanel implements ActionListener, AsynchronousCallback {
	private static final long serialVersionUID = 1L;

	private Main application;
	private JButton encrypt;
	private JTextField receiver;
	private ChooseFilePanel choosePlainFile;
	private ChooseFilePanel saveCipherFile;
	private SignPanel signPanel;
	private JProgressBar progressBar;
	private JComboBox method;

	public EncryptPanel(Main application) {
		setLayout(new BoxLayout(this, Y_AXIS));
		this.application = application;
		encrypt = new JButton(getString("EncryptPanel.encrypt")); //$NON-NLS-1$
		encrypt.addActionListener(this);
		receiver = new JTextField(15);
		choosePlainFile = new ChooseFilePanel(getString("EncryptPanel.encryptFile")); //$NON-NLS-1$
		saveCipherFile = new ChooseFilePanel(getString("EncryptPanel.savePath")); //$NON-NLS-1$
		String recentPlain = Main.getUserConfig().getProperty(LAST_PLAIN_OPEN_FOLDER);
		if (recentPlain != null)
			choosePlainFile.setCurrentDirectory(new File(recentPlain));
		
		String recentCipher = Main.getUserConfig().getProperty(LAST_CIPHER_SAVE_FOLDER);
		if (recentCipher != null)
			saveCipherFile.setCurrentDirectory(new File(recentCipher));
		
		saveCipherFile.setFileFilter(new FileNameExtensionFilter(getString("EncryptPanel.ibeExtention"), "ibe")); //$NON-NLS-1$ //$NON-NLS-2$
		signPanel = new SignPanel();
		JPanel receiverPanel = new JPanel(new FlowLayout(LEFT));
		receiverPanel.add(new JLabel(getString("EncryptPanel.alice"))); //$NON-NLS-1$
		receiverPanel.add(receiver);
		progressBar = new JProgressBar();
		progressBar.setValue(0);
		JPanel progressPanel = new JPanel(new FlowLayout(LEFT));
		progressPanel.add(new JLabel(getString("EncryptPanel.encryptProgress"))); //$NON-NLS-1$
		progressPanel.add(progressBar);
		List<CryptMethod> ms = application.getMethods();
		String[] methodNames = new String[ms.size()];
		for (int i = 0; i < methodNames.length; i++)
			methodNames[i] = ms.get(i).getName();
		method = new JComboBox(methodNames);
		JPanel methodPanel = new JPanel(new FlowLayout(LEFT));
		methodPanel.add(new JLabel(getString("EncryptPanel.encryptMethod"))); //$NON-NLS-1$
		methodPanel.add(method);
		JPanel buttonPanel = new JPanel(new FlowLayout(RIGHT));
		buttonPanel.add(encrypt);
		add(choosePlainFile);
		add(receiverPanel);
		add(methodPanel);
		add(saveCipherFile);
		add(progressPanel);
		add(signPanel);
		add(buttonPanel);
		setVisible(true);
	}

	@Override
	public void progressUpdated(int progress) {
		if (progress < 0)
			this.progressBar.setValue(0);
		else if (progress <= 100)
			this.progressBar.setValue(progress);
		else
			this.progressBar.setValue(100);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == this.encrypt) {
			// 验证加密文件
			File plain = new File(choosePlainFile.getSelectedFilePath());
			if (!plain.exists()) {
				showMessageDialog(application, getString("EncryptPanel.plainNotExist"), getString("EncryptPanel.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				return;
			}
			if (!plain.canRead()) {
				showMessageDialog(application, getString("EncryptPanel.plainCannotRead"), getString("EncryptPanel.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				return;
			}
			// 验证接收者
			String alice = receiver.getText();
			if (alice == null || alice.trim().length() < 1) {
				showMessageDialog(application, getString("EncryptPanel.aliceNotNull"), getString("EncryptPanel.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				return;
			}
			// 验证保存路径
			String cipherPath = saveCipherFile.getSelectedFilePath();
			if (!cipherPath.toLowerCase().endsWith(".ibe")) {
				cipherPath += ".ibe";
			}
			File cipher = new File(cipherPath);
			if (cipher.exists()) {
				if (!cipher.canWrite()) {
					showMessageDialog(application, getString("EncryptPanel.cannotWriteCipher"), getString("EncryptPanel.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					return;
				}
				// 提示是否覆盖
				String format = getString("EncryptPanel.confirmRewrite"); //$NON-NLS-1$
				int rewrite = showConfirmDialog(application, String.format(format, cipher.getName()), getString("EncryptPanel.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$
				if (rewrite != JOptionPane.YES_OPTION)
					return;
			} else {
				try {
					if (!cipher.createNewFile())
						throw new IOException();
				} catch (IOException error) {
					error.printStackTrace();
					showMessageDialog(application, getString("EncryptPanel.cannotCreateCipher"), getString("EncryptPanel.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					return;
				}
			}
			if (!cipher.canWrite()) {
				showMessageDialog(application, getString("EncryptPanel.cannotWriteCipher"), getString("EncryptPanel.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				return;
			}
			String crypto = application.getMethodIdByName((String) method.getSelectedItem());
			
			SymmetricEncryptManager encryptManager = new AsymmetricEncryptManager(this, crypto, plain, alice, cipher);
			// 验证是否签名
			if (signPanel.signEnabled()) {
				File ibcID = new File(signPanel.getCertPath());
				if (!isValidIDFile(ibcID)) {
					showMessageDialog(application, getString("EncryptPanel.invalidIBCFile"), getString("EncryptPanel.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					return;
				}
				encryptManager.setIbcID(ibcID);
			}
			// 启动一个新的线程完成加密工作
			Thread encrypt = new Thread(encryptManager);
			encrypt.start();

			Main.getUserConfig().setProperty(LAST_PLAIN_OPEN_FOLDER, plain.getParent());
			Main.getUserConfig().setProperty(LAST_CIPHER_SAVE_FOLDER, cipher.getParent());
			if (signPanel.signEnabled())
				Main.getUserConfig().setProperty(DEFAULT_ID_FILE, signPanel.getCertPath());
		}
	}

	@Override
	public void message(String content, int type) {
		switch (type) {
		case JOptionPane.ERROR_MESSAGE:
			showMessageDialog(application, content, getString("EncryptPanel.error"), type); //$NON-NLS-1$
			break;
		case JOptionPane.INFORMATION_MESSAGE:
			showMessageDialog(application, content, getString("EncryptPanel.information"), type); //$NON-NLS-1$
			break;
		case JOptionPane.WARNING_MESSAGE:
			showMessageDialog(application, content, getString("EncryptPanel.warning"), type); //$NON-NLS-1$
			break;
		case JOptionPane.QUESTION_MESSAGE:
			showMessageDialog(application, content, getString("EncryptPanel.question"), type); //$NON-NLS-1$
			break;
		case JOptionPane.PLAIN_MESSAGE:
			showMessageDialog(application, content, "", type); //$NON-NLS-1$
			break;
		default:
			if (type == AsynchronousCallback.TITLE_MESSAGE) {
				application.setTitle(content);
			}
			break;
		}
	}

	/**
	 * 判断是否是合法的IBC ID文件
	 * @param idFile 要判断的文件
	 * @return 只有合法的ID文件返回true 非法文件或者文件不可读返回false
	 */
	public static final boolean isValidIDFile(File idFile) {
		try {
			IDIO idio = new SimpleIDFileIOImpl(idFile);
			idio.load();
			return true;
		} catch (IBCIOException e) {
			return false;
		}
	}
}
